#include "WaterCalc.h"
#include "Cell.h"

std::vector<std::vector<int>> matrix;
std::vector<int> minCol;
std::vector<int> minRow;

void printMinCols()
{
	for (auto it = minCol.begin(); it != minCol.end(); ++it){
		//std::cout << "minCols " <<std::endl;
	}
}

int getBordersMinHeight(int row, int col);
inline int Min(int a, int b){
	return (a <= b ? a : b); 
}
MinBorders::MinBorders(int minRow,int minCol)
{
	this->rowMin = minRow;
	this->colMin = minCol;

}
Index::Index(int row, int col)
{
	this->row = row;
	this->col = col;
}
//Cell::Cell(){
//	this->type = EMPTY;
//}
//Cell::Cell(Cell::Type t){
//	this->type = t;
//}
//void Cell::Set(Cell::Type t)
//{
//	this->type = t;
//}
//void Cell::Print(){
//	switch(this->type){
//	case BORDER:
//		std::cerr << "B " ; 
//		break;
//	case EMPTY:
//		std::cerr << "E " ;
//		break;
//	case FILLED:
//		std::cerr << "F " ;
//		break;
//	}
//}
std::vector<std::vector<Cell*>> CellMatrix;

void VisitFilling(int row, int col, int* minBorderH, std::stack<Index*> &visitedStack)
{
	if (CellMatrix[row][col]->type == Cell::BORDER || CellMatrix[row][col]->type == Cell::FILLED )
		return;
	int minB = getBordersMinHeight(row, col);
	if (minB != -1){
		*minBorderH = Min (*minBorderH, minB);
	}
	CellMatrix[row][col]->Set(Cell::FILLED);
	visitedStack.push(new Index(row,col));
//	if (matrix[row][col] >= matrix[row + 1][col])
		VisitFilling(row + 1, col, minBorderH, visitedStack);
//	if (matrix[row][col] >= matrix[row - 1][col])
		VisitFilling(row - 1, col, minBorderH,visitedStack);
//	if (matrix[row][col] >= matrix[row][col +1])
		VisitFilling(row, col + 1, minBorderH,visitedStack);
//	if (matrix[row][col] >= matrix[row][col - 1])
		VisitFilling(row, col - 1, minBorderH,visitedStack);
		
}
void WaterCalculator::Volume()
{
	int row = matrix.size();
	int col = matrix[0].size();

	

	for (int i = 0 ; i < row; i++){
		std::vector<Cell*> cellRow;
		for (int j = 0 ; j < col; j++){
			if (i == 0 || j == 0 || i == row -1 || j == col -1){
				Cell *c = new Cell(Cell::BORDER);
				cellRow.push_back(c);
			}else{
				if (matrix[i][j]>= matrix[i][0] && matrix[i][j]>= matrix[i][col - 1] &&
					matrix[i][j]>= matrix[0][j] && matrix[i][j]>= matrix[row - 1][j]){
				
				//if (matrix[i][j]>= minCol[j] && matrix[i][j]>= minRow[i]){
					cellRow.push_back(new Cell(Cell::BORDER));
				}else{
					cellRow.push_back(new Cell(Cell::EMPTY));
				}
			}
		}
		CellMatrix.push_back(cellRow);
	}

	for (int i = 0 ; i < row ; i++){
		for (int j = 0 ; j < col; j++){
			CellMatrix[i][j]->Print();
		}
		std::cerr << std::endl;
	}
	for (int i = 1 ; i < row -1; i++){
		for (int j = 1 ; j < col -1; j++){
			if (CellMatrix[i][j]->type == Cell::EMPTY){
				int minN =  getBordersMinHeight(i, j);
				if (minN!= -1 && minN <= matrix[i][j]){
					CellMatrix[i][j]->Set(Cell::BORDER);
				}
			}
		}
	}
	std::cerr << std::endl;
	for (int i = 0 ; i < row ; i++){
		for (int j = 0 ; j < col; j++){
			CellMatrix[i][j]->Print();
		}
		std::cerr << std::endl;
	}
	int max_height = 1000;//std::numeric_limits<int>::max();
	int* ptr =&max_height;
	int volume = 0;
	
	for (int i = 1 ; i < row - 1; i++){
		for (int j = 1 ; j < col -1 ; j++){
			std::stack<Index*> stackVisited = std::stack<Index*>();
			VisitFilling(i,j,&max_height, stackVisited);
		
			while(!stackVisited.empty()){
				Index *p = stackVisited.top();
				std::cerr << "max height " << max_height << "\n";
				int val = matrix[p->row][p->col];
				int v = max_height - matrix[p->row][p->col];
				if (v > 0)
					volume += v;
				stackVisited.pop();
				delete p;
			}
			max_height = 1000 ;//std::numeric_limits<int>::max();

		}
	}
	//VisitFilling(1,4,&max_height, stackVisited);
	std::cerr << std::endl;
	for (int i = 0 ; i < row ; i++){
		for (int j = 0 ; j < col; j++){
			CellMatrix[i][j]->Print();
		}
		std::cerr << std::endl;
	}
	/*while(!stackVisited.empty()){
		Index *p = stackVisited.top();
		int v = max_height - matrix[p->row][p->col];
		volume += v;
		stackVisited.pop();
		delete p;
	}*/
	std::cerr << "\n min " << max_height << " volume " << volume << std::endl; 
}
void const  WaterCalculator::printMatrix() const{
	for (std::vector<std::vector<int>>::iterator it = matrix.begin(); it!=matrix.end(); ++it) {
		for (std::vector<int>::iterator it2 = it->begin() ; it2!= it->end(); ++it2) {
			printf("%3d", *it2);//std::cout  << *it2 << " ";
		}
		std::cout << std::endl;
	}
}

std::vector<Cell::Direction> * GetEmptyNeighsDirections(int row, int col)
{
	std::vector<Cell::Direction> *emptyN = new std::vector<Cell::Direction>();
	if (CellMatrix[row][col - 1]->type == Cell::EMPTY){
		emptyN->push_back(Cell::LEFT);
	}
	if (CellMatrix[row][col + 1]->type == Cell::EMPTY){
		emptyN->push_back(Cell::RIGHT);
	}
	if (CellMatrix[row - 1][col]->type == Cell::EMPTY){
		emptyN->push_back(Cell::UP);
	}
	if (CellMatrix[row + 1][col]->type == Cell::EMPTY){
		emptyN->push_back(Cell::DOWN);
	}
	return emptyN;
}
std::vector<Cell::Direction> * GetBordersDirections(int row, int col){
	std::vector<Cell::Direction> *borderDirections = new std::vector<Cell::Direction>();
	if (CellMatrix[row][col - 1]->type == Cell::BORDER){
		borderDirections->push_back(Cell::LEFT);
	}
	if (CellMatrix[row][col + 1]->type == Cell::BORDER){
		borderDirections->push_back(Cell::RIGHT);
	}
	if (CellMatrix[row - 1][col]->type == Cell::BORDER){
		borderDirections->push_back(Cell::UP);
	}
	if (CellMatrix[row + 1][col]->type == Cell::BORDER){
		borderDirections->push_back(Cell::DOWN);
	}
	return borderDirections;
} 

//returns the minimum height of the borders connected to cell at row, col . If no border
//is connected return -1.
int getBordersMinHeight(int row, int col)
{
	std::vector<Cell::Direction> *borderDirections = GetBordersDirections(row, col);

	if (borderDirections->size() == 0)
		return -1;
	int min = std::numeric_limits<int>::max();

	for(unsigned short int i = 0 ; i < borderDirections->size(); i++){
		switch(borderDirections->at(i)){
		case Cell::UP:
			min = Min (min, matrix[row -1][col]);
			break;
		case Cell::DOWN:
			min = Min (min, matrix[row +1][col]);
			break;
		case Cell::LEFT:
			min = Min (min, matrix[row][col -1]);
			break;
		case Cell::RIGHT:
			min = Min (min, matrix[row][col + 1]);
			break;
		}
	}
	delete borderDirections;
	return min;
}

//return true if the cell at row r, col c is adjacent to al test one border
bool IsNearBorder(int r, int c){
	int row = matrix.size();
	int col = matrix[0].size();


	if (r == 1 || c == 1 || r == row -2 || c == col -2)
		return true;
	if (CellMatrix[row][col - 1]->type == Cell::BORDER ||
		CellMatrix[row][col + 1]->type == Cell::BORDER ||
		CellMatrix[row - 1][col]->type == Cell::BORDER ||
		CellMatrix[row + 1][col ]->type == Cell::BORDER){
		return true;
	}else{
		return false;
	}
}
void WaterCalculator::ParseMatrixFile() const
{
	std::string line;
	std::ifstream fileStream;
	//fileStream.open ("MatrixExample.txt");
	fileStream.open ("container0.txt");
	//fileStream.open ("input12.txt");
	int lineCount = 0;
	int firstLineColumnCount = 0;
	for (; true != fileStream.eof();lineCount++){
		std::getline(fileStream, line);
		std::istringstream tokenizer(line);
        std::string token;

        std::vector<int> integerLine;
		if (lineCount == 0){
			while(!tokenizer.eof()){
				getline(tokenizer, token, ' ');
				std::istringstream number_string(token);
				int integer;
				number_string >> integer;
				char c;
				if (number_string.fail() || number_string.get(c)) {
			        // not an integer
					//return false;
					std::cout << "not a number\n";
					throw ParseMatrixException();
				}
				integerLine.push_back(integer);
				minCol.push_back(integer);
				if (firstLineColumnCount == 0){
					minRow.push_back(integer);
				}else{
					minRow[0] = Min(minRow[0], integer);
				}
				
				firstLineColumnCount++;
				
			}
		}else{
			int columnCount = 0;
			while(!tokenizer.eof()){
				getline(tokenizer, token, ' ');
				std::istringstream number_string(token);
				int integer;
				number_string >> integer;
				char c;
				if (number_string.fail() || number_string.get(c)) {
			        // not an integer
					//return false;
					std::cout << "not a number\n";
				}
				integerLine.push_back(integer);

				minCol[columnCount] = Min(minCol[columnCount], integer);
				if (columnCount == 0){
					minRow.push_back(integer);
				}else{
					minRow[0] = Min(minRow[0], integer);
				}
				if (columnCount > firstLineColumnCount){
					std::cout << "error\n";
				}
				++columnCount;
			}
		}
		matrix.push_back(integerLine);
	//	int foo = matrix[3][4];
	}
	std::cerr << "line count " << lineCount <<  " col "  << firstLineColumnCount << "\n" ;
	 
}


